package de.recipeminer.crawling.chefkoch;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import de.recipeminer.persistence.Db4oCompanionProvider;
import de.recipeminer.persistence.Db4oContainers;
import de.recipeminer.persistence.Db4oObjectCompanion;
import de.recipeminer.persistence.Db4oSingletonHelper;
import org.apache.log4j.Logger;

import java.util.Collections;
import java.util.Map;
import java.util.Set;

public class ChefkochCrawlingState implements Db4oCompanionProvider<ChefkochCrawlingState> {
    private static Logger logger = Logger.getLogger(ChefkochCrawlingState.class);
    private transient Db4oObjectCompanion<ChefkochCrawlingState> db4oCompanion;
    protected Set<String> crawledShowIDs;
    protected Map<String, String> failedShowIDs; //showId, detailedMessage
    protected int lastSeqQueryIndex;
    protected int duplicateShowIDs;
    private static ChefkochCrawlingState instance = null;

    private ChefkochCrawlingState() {
        reset();
    }

    public static ChefkochCrawlingState getInstance() {
        if (instance == null || instance.isStale()) {
            ChefkochCrawlingState persistedInstance =
                    Db4oSingletonHelper.getPersistedInstance(ChefkochCrawlingState.class);
            instance = persistedInstance != null ? persistedInstance : new ChefkochCrawlingState();
        }
        return instance;
    }

    public boolean recipeCrawled(String showID) {
        if (crawledShowIDs.contains(showID)) {
            duplicateShowIDs++;
            String msg = String.format("AbstractRecipe with ShowID '%s' already crawled " +
                    "(%d duplicated so far", showID, duplicateShowIDs);
            logger.warn(msg);
            return false;
        } else {
            crawledShowIDs.add(showID);
            return true;
        }
    }

    public void recipeDownloadFailed(String showID, String errorMessage) {
        if (crawledShowIDs.contains(showID)) duplicateShowIDs++;
        failedShowIDs.put(showID, errorMessage);
    }

    public int getLastQueryIndex() {
        return lastSeqQueryIndex;
    }

    public void setLastQueryIndex(int lastSeqQueryIndex) {
        this.lastSeqQueryIndex = Math.max(lastSeqQueryIndex, this.lastSeqQueryIndex);
    }

    public Set<String> getCrawledShowIDs() {
        return Collections.unmodifiableSet(crawledShowIDs);
    }

    public int getCrawledCount() {
        return crawledShowIDs.size();
    }

    public int getDuplicateCount() {
        return duplicateShowIDs;
    }

    public Map<String, String> getFailedDownloads() {
        return Collections.unmodifiableMap(failedShowIDs);
    }

    public void reset() {
        crawledShowIDs = Sets.newHashSet();
        failedShowIDs = Maps.newHashMap();
        lastSeqQueryIndex = 0;
        duplicateShowIDs = 0;
    }

    private boolean isStale() {
        if (instance != null) {
            return !Db4oContainers.getDefault().ext().isStored(this);
        } else {
            return true;
        }
    }

    @Override
    public Db4oObjectCompanion getDb4oCompanion() {
        if (db4oCompanion == null) {
            db4oCompanion = new Db4oObjectCompanion<ChefkochCrawlingState>(this);
        }
        return db4oCompanion;
    }

    @Override
    public boolean validate() {
        return true;
    }
}
